//General
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;


//Project
using CS6238Project2;
using CS6238Project2.Banking.Commerce;
using CS6238Project2.Banking.Communication;
using CS6238Project2.Banking.Tools;
using CS6238Project2.Banking.Entity;

//Serialization
using System.IO;
using System.Xml;
using System.Xml.Serialization;

using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

namespace CS6238Project2.GUI
{
    [Serializable]
    public partial class UserDisplay : Form
    {
        /*
         * TODO:
         *  Network:
         *      Handling of receiver
         *      Handling of Transmitter
         *      Handling of Network Config changing receiver/transmitter
         *            -Use the networkWindow_ButtonClicked method.
         *            -e contains all necessary data.
         *  
         * Must handle parcel sending for all these functons:
         * 
                //resyncToolStripMenuItem_Click
                //inventoryOrderButton_Click
         * 
         *      //merchantListBox_SelectedValueChanged
	            //merchantOpenAccountButton_Click
         * 
	            //accountTransactionWithdrawButton_Click
	            //accountTransactionDepositButton_Click
         *      //accountOpenAccountButton_Click
         * 
         * 
	            //inventoryOrderButton_Click
	            //merchantOpenAccountButton_Click
         * 
         * 
         */

        User user;
        int currentUserInventoryRow;
        NetworkControlForm networkWindow;//network controls
        public Server server;
        private Parcel globalParcel;

        public User User
        {
            set { user = value; }
            get { return user; }
        }

        


        public UserDisplay()
        {
            Random random = new Random();
            server = new Server(random.Next(10000, 20000), false);
            user = new User();
            user.Account = new Account();
            server.ParcelPassed += new Server.ParcelDelegate(Parcel_Received);

            InitializeComponent();
            networkWindow = new NetworkControlForm();//set up the network control
            userBindingSource.DataSource = user;


            recordBindingSource.DataSource = user.Account.Record;
            //recordGridView.DataSource = user.Account.Record;
        }



        /************************************************************************************/
        /***                        GLOBAL EVENT HANDLING                                 ***/
        /************************************************************************************/


        // used to make thread safe calls
        private Thread dataThread;


        /// <summary>
        /// Updates the binding source for the selected merchant's inventory with the global data
        /// </summary>
        private void UpdateMerchantsInventory()
        {
            if (globalParcel.InventoryList != null)
            {
                if (globalParcel.InventoryList.Count > 0)
                {
                    user.Goods = globalParcel.InventoryList;
                    merchantsInventoryGridView.DataSource = user.Goods;
                }
            }
        }
        /// <summary>
        /// Updates the binding source for the list of merchants with the global data
        /// </summary>
        private void UpdateMerchantsList()
        {
            if (globalParcel.NameList != null)
            {
                if (globalParcel.NameList.Count > 0)
                {
                    user.Merchants = globalParcel.NameList;
                    merchantListBox.DataSource = user.Merchants;
                }
            }
        }
        /// <summary>
        /// Updates the binding source for the transaction record with the global data
        /// </summary>
        private void UpdateTransactionsRecord()
        {
            if (globalParcel.Account != null)
            {
                user.Account = globalParcel.Account;
                if (globalParcel.Account.Record.Count > 0)
                {
                    
                    recordGridView.DataSource = user.Account.Record;
                }
            }
        }

        private void Parcel_Received(Parcel parcel)
        {
            Console.WriteLine(
                "Sender: " + parcel.Sender +
                "\nReceiver: " + parcel.Receiver +
                "\nType: " + parcel.Type +
                "\nMessageData: " + parcel.MessageData + "\n"
            );
            globalParcel = parcel;
            dataThread = new Thread(new ThreadStart(this.ThreadProcSafe));
            this.dataThread.Start();
        }


        // This method is executed on the worker thread and makes
        // a thread-safe call on the MainApp control.
        private void ThreadProcSafe()
        {

            if ((globalParcel.SenderPort == 8000) && 
                (globalParcel.Type == "Registration Response"))
            {
                if (recordGridView.InvokeRequired)
                {
                    recordGridView.Invoke(new MethodInvoker(ProcessParcel));
                    
                }
                else
                {
                    ProcessParcel();
                }
            }
            else if (globalParcel.Type == "List Response")
            {
                //Inventory of selected merchant
                if (merchantsInventoryGridView.InvokeRequired)
                {
                    merchantsInventoryGridView.Invoke(new MethodInvoker(UpdateMerchantsInventory));
                }
                else
                {
                    UpdateMerchantsInventory();
                }

                //List of merchants
                if (merchantListBox.InvokeRequired)
                {
                    merchantListBox.Invoke(new MethodInvoker(UpdateMerchantsList));
                }
                else
                {
                    UpdateMerchantsList();
                }

                //Transaction Record
                if (recordGridView.InvokeRequired)
                {
                    recordGridView.Invoke(new MethodInvoker(UpdateTransactionsRecord));
                }
                else
                {
                    UpdateTransactionsRecord();
                }
            }
            else if (globalParcel.Type == "Order response")
            {
                if (merchantsInventoryGridView.InvokeRequired)
                {
                    merchantsInventoryGridView.Invoke(new MethodInvoker(ProcessParcel));
                }
                else
                {
                    ProcessParcel();
                }
            }
            else
            {
                ProcessParcel();
            }

            
            balanceTextBox.Invoke(new MethodInvoker(UpdateBalance));
            

            


        }



        /************************************************************************************/
        /***                          HANDLE ACCOUNT CONTROLS                             ***/
        /************************************************************************************/
        /// <summary>
        /// Withdraw a value listed from one's account
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void accountTransactionWithdrawButton_Click(object sender, EventArgs e)
        {
            try
            {
                int amount = -1 * Convert.ToInt32(transactionTextBox.Text);

                Parcel parcel = user.Transact(amount);
                server.Send(IP(), Port(), parcel);
                parcel.SenderAddr = IP();
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO BANK
            //INCOMPLETE
        }

        private void UpdateBalance()
        {
            balanceTextBox.Text = Convert.ToString(user.Account.Balance);
        }

        /// <summary>
        /// Deposit the amount typed into the transactionTextBox
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void accountTransactionDepositButton_Click(object sender, EventArgs e)
        {
            try
            {
                int amount = Convert.ToInt32(transactionTextBox.Text);
                Parcel parcel = user.Transact(amount);
                server.Send(IP(), Port(), parcel);
                parcel.SenderAddr = IP();
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO BANK
            //INCOMPLETE
        }


        /// <summary>
        /// Handles the action of opening an account with the bank
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void accountOpenAccountButton_Click(object sender, EventArgs e)
        {
            try
            {  
                Parcel parcel = user.Register("Bank of America");
                parcel.SenderAddr = IP();
                server.Send(IP(), Port(), parcel);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO BANK
            //INCOMPLETE
        }

        /************************************************************************************/
        /***                          HANDLE RECORD TABLE                                 ***/
        /************************************************************************************/


        /*
         * TODO:
         *  Request the bank send the transaction record again
         * 
         */

        /// <summary>
        /// Resync the record with the bank
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void resyncToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //Send a request to the bank for hte global list of Merchants and
            //the record for that account
            try
            {
                Parcel parcel = new Parcel(user.Name, "Bank of America", "Request");
                parcel.MessageData = "Lists";
                server.Send(IP(), Port(), parcel);
           
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO BANK
            //INCOMPLETE
        }

        /************************************************************************************/
        /***                    HANDLE MERCHANT BOX                                       ***/
        /************************************************************************************/

        /// <summary>
        /// Registers for a merchant that's clicked on in the list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void merchantOpenAccountButton_Click(object sender, EventArgs e)
        {
            try
            {
                string name = ((AddressEntry)merchantListBox.SelectedItem).Name;
                Parcel parcel = user.Register(name);
                parcel.SenderAddr = IP();
                server.Send(IP(), Port(), parcel);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO MERCHANT
            //INCOMPLETE
        }

        /// <summary>
        /// Changes the inventory based on the selected merchant
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void merchantListBox_SelectedValueChanged(object sender, EventArgs e)
        {
            try
            {
                AddressEntry item = (AddressEntry) merchantListBox.SelectedItem;                
                Parcel parcel = new Parcel();
                parcel.Sender = user.Name;
                parcel.Receiver = item.Name;
                parcel.Type = "List Request";
                parcel.SenderAddr = IP();
                parcel.Number = user.Account.Number;
                server.Send(IP(), 8000, parcel);
            
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
            //SEND PARCEL TO MERCHANT
            //INCOMPLETE
        }


        /************************************************************************************/
        /***                    HANDLE USER INVENTORY GRIDVIEW                            ***/
        /************************************************************************************/


        /// <summary>
        /// Grab the quantity and submit an order
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void inventoryOrderButton_Click(object sender, EventArgs e)
        {
            try
            {
                int quantity = Convert.ToInt32(inventoryOrderTextBox.Text);
                int worth = (int)merchantsInventoryGridView[1, currentUserInventoryRow].Value;
                string name = (string)merchantsInventoryGridView[0, currentUserInventoryRow].Value;
                string merchantName = ((AddressEntry)merchantListBox.SelectedItem).Name;
                Parcel parcel = user.Order(merchantName, name, quantity, worth);
                parcel.SenderAddr = IP();
                server.Send(IP(), Port(), parcel);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO MERCHANT
            //INCOMPLETE
        }

        /// <summary>
        /// sets the global selected index
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void userInventoryGridView_RowEnter(object sender, DataGridViewCellEventArgs e)
        {
            try
            {
                currentUserInventoryRow = e.RowIndex;
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }


        /************************************************************************************/
        /***                             PASSIVE FUNCTIONS                                ***/
        /************************************************************************************/


        //I am unsure how these should work, but something like this:

        /// <summary>
        /// Processes incoming information, after the receiver turns the incoming data
        /// into parcels to present to this function
        /// </summary>
        /// <param name="parcel">The incoming parcel</param>
        private void ProcessParcel()
        {
            Parcel parcel = new Parcel(globalParcel);
            bool gridRefresh = false;
            Parcel refreshParcel = null;
            try
            {
                ParcelError(parcel);//scan the parcel for errors to pop up
                //Check if we're just spooling in new lists
                if (parcel.Type == "List Response")
                {
                    try
                    {
                        if (parcel.Sender == "Bank of America")
                        {
                            user.Account = parcel.Account;
                            recordGridView.DataSource = user.Account.Record;
                            user.Merchants = parcel.NameList;
                        }
                        else
                        {
                            user.Goods = parcel.InventoryList;
                            parcel.Type = "Terminate";
                        }
                    }
                    catch (Exception exception)
                    {
                        Console.WriteLine(exception);
                    }
                }


                //Check to see if we need to refresh the account information
                if ((parcel.Type == "Transaction Response") ||
                    (parcel.Type == "Dual Transaction Response") ||
                    (parcel.Type == "Registration Response"))
                {
                    gridRefresh = true;
                    if (parcel.MessageData == "Approved")
                    {
                        //Just to note.
                        if (parcel.Type == "Registration Response") { MessageBox.Show("Registration Approved!"); }
                        if (parcel.Account == null)
                        {
                            refreshParcel = new Parcel(parcel);
                        }
                        else
                        {
                            //MEOW!!!! why is this here?
                        }
                    }
                }
                

                
                //Since we're not, it can be handled by the User object.
                parcel = user.ProcessParcel(parcel);

                if (gridRefresh)
                {
                    recordGridView.DataSource = user.Account.Record;
                }

                if (null != refreshParcel)
                {
                    refreshParcel.Receiver = refreshParcel.Sender;
                    refreshParcel.Sender = user.Name;
                    refreshParcel.Type = "List Request";
                    refreshParcel.Number = user.Account.Number;
                    parcel.SenderAddr = IP();
                    server.Send(IP(), parcel.SenderPort, refreshParcel);
                }


                //THERE ARE NO SPECIAL ACTIONS TO DO
                //SEND PARCEL TO parcel.Receiver
                //INCOMPLETE
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }

        }





        private int Port()
        {
            return 8000;
        }

        private string IP()
        {
            return "192.168.0.101";
        }


        /// <summary>
        /// Checks to see if a parcel contains a reject or some such, and if so
        /// will pop that error up.
        /// </summary>
        /// <param name="parcel"></param>
        private void ParcelError(Parcel parcel)
        {
            //This is an array that we can easily enter new error
            //messages into, and the error handling will otherwise
            //be unchanged
            string[] errors = 
                {
                    "Denied", 
                    "Account Not Found",
                    "Transaction data invalid",
                    "Exception Occurred",
                    "Transaction Not Approved",
                    "Invalid Parcel",
                    "User was not approved!"
                };

            //Cycle through the list, seeking anything appropriate.
            for (int i = 0; i < errors.Length; i++)
            {
                if (parcel.MessageData == errors[i])
                {
                    MessageBox.Show(errors[i]);
                }
            }


        }
        /************************************************************************************/
        /***                                NETWORK CONTROLS                              ***/
        /************************************************************************************/


        private void connectionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            /*
             * TODO:
             *  When first created, get port data from the transmitter/receiver
             *  When first created, determine connection status (virtual)
             * 
             */
            
            //networkWindow.listeningPort = 
            //networkWindow.sendingPort =
            networkWindow.Show();
            

        }



        private void testBox()
        {

            networkWindow.NetworkEvent +=
                new NetworkControlForm.
                NetworkControlHandler(networkWindow_ButtonClicked);
            networkWindow.Show();

        }

        private void networkWindow_ButtonClicked(object sender, NetworkControlEventArgs e)
        {
            bool DeterminesWhatConnectionButtonSays = e.Connected;
            int thisIsTheListeningPortValue = e.ListeningPort;
            int thisIsTheSendingPortValue = e.SendingPort;
            //Update network settings

            //INCOMPLETE
        }


        


        private void recordGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            /*
             * TODO:
             *  NOTHING
             */
            try
            {
                Transaction trans = user.Account.Record[e.RowIndex];
                if (e.ColumnIndex == 3)
                {
                    
                    if (trans.Adjustment < 0)
                    {

                        recordGridView[e.ColumnIndex, e.RowIndex].Style.ForeColor = Color.Red;
                    }
                    else if (trans.Adjustment > 0)
                    {

                        recordGridView[e.ColumnIndex, e.RowIndex].Style.ForeColor = Color.Green;
                    }
                    else
                    {

                        recordGridView[e.ColumnIndex, e.RowIndex].Style.ForeColor = Color.Black;
                    }
                }
            }
            catch (ArgumentOutOfRangeException exception)
            {
                Console.WriteLine(exception);
            }
        }



        /// <summary>
        /// Uses serialization to save the entire kernel as a file of itself.
        /// </summary>
        /// <param name="fileName">The name for the file to be saved as.</param>
        private void save()
        {

            Stream stream = null;
            try
            {
                IFormatter formatter = new BinaryFormatter();
                stream = new FileStream(user.Name + ".UserFile", FileMode.Create, FileAccess.Write, FileShare.None);
                List<object> serialized = new List<object>();



                String xmlString = Encryption.XmlString(user);
                formatter.Serialize(stream, xmlString);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            finally
            {
                if (null != stream)
                    stream.Close();
            }
        }

        private void UserDisplay_FormClosing(object sender, FormClosingEventArgs e)
        {
            save();
        }

        private void purchasesGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            string name;

            try
            {
                if (e.ColumnIndex == 0)
                {
                    name = user.Purchases[e.RowIndex].Good.Name;
                    purchasesGridView[e.ColumnIndex, e.RowIndex].Value = name;
                }
            }

            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
        }


      
    }
}